/********************************************************************
WeatherStation Class (original model) for starting the
Weather Station executable for monitoring weather via the
OneWire Hardware
A class by Lou Rosas
********************************************************************/
package rosas.lou.weatherclasses;

import java.util.*;
import java.lang.*;
import rosas.lou.weatherclasses.*;
import gnu.io.*;

import com.dalsemi.onewire.*;
import com.dalsemi.onewire.adapter.*;
import com.dalsemi.onewire.container.*;

//
//
//
public class WeatherStation implements Observer{
   private static final int defaultUpdateRate = 10; //10 minutes

   private Hashtable sensorHash;
   private Hashtable tempSensorHash;
   private int updateRate;
   private Stack tempSensorStack;
   private boolean quit;

   //**********************Public Methods****************************
   /*****************************************************************
   Constructor of no arguments
   *****************************************************************/
   public WeatherStation(){
      this.sensorHash     = new Hashtable();//Might not be needed
      this.tempSensorHash = new Hashtable();//Might not be needed
      //For multiple temperature sensors
      this.tempSensorStack = new Stack();
      //Set to run indefinitely
      this.setQuit(false);
      //Set the update rate to 10 min.
      this.setUpdateRate(defaultUpdateRate);
      this.findSensorInformation();
      this.findTempSensorInformation();
      this.setUpTemperatureSensors();
      //Set the Units after all the sensors are set up
      this.setUnitsEnglish();  //Be default, set the units to English
      this.runSystem();
   }

   /*****************************************************************
   Get the current update rate in minutes
   *****************************************************************/
   public int getUpdateRate(){
      return this.updateRate;
   }

   /*****************************************************************
   Run the weather station by polling the sensors at a specific
   rate
   *****************************************************************/
   public void runSystem(){
      Calendar cal   = Calendar.getInstance();
      double temp    = -1000.;
      int currentMinute;
      int lastMinute = -1;
      int i          = 0;
      int updateRate = this.getUpdateRate();
      //Run continuously until told otherwise
      while(!this.getQuit()){
         try{
            Thread.sleep(1000);  //Sleep for one second
         }
         catch(InterruptedException ie){}
         cal.setTimeInMillis(System.currentTimeMillis());
         currentMinute = cal.get(Calendar.MINUTE);
         if(currentMinute != lastMinute){
            if((i % updateRate) == 0){
               Enumeration e = this.tempSensorStack.elements();
               while(e.hasMoreElements()){
                  TemperatureSensor ts;
                  ts = (TemperatureSensor)e.nextElement();
                  temp = ts.getTemperature();
                  System.out.println(cal.getTime());
                  System.out.println("Temp:  " + temp + " deg F\n");
               }
            }
            lastMinute = currentMinute;
            ++i;
         }
      }
      Enumeration e = this.tempSensorStack.elements();
      while(e.hasMoreElements()){
         TemperatureSensor ts = (TemperatureSensor)e.nextElement();
         ts.freePort(); //Need to free the port!!!
      }
   }

   /*****************************************************************
   Set the quit boolean
   *****************************************************************/
   public void setQuit(boolean toQuit){
      this.quit = toQuit;
   }

   /*****************************************************************
   Set the units to absolute values:  really, this is only used
   for the temperature data:  this sets the units from the
   temperature sensor to Kelvin
   *****************************************************************/
   public void setUnitsAbsolute(){
      //Set the temperature units first
      this.setTemperatureUnits(TemperatureSensor.KELVIN);
   }

   /*****************************************************************
   Set the Units to Metric:  primarioy for temperature related
   data
   *****************************************************************/
   public void setUnitsMetric(){
      //Set the temperature units first
      this.setTemperatureUnits(TemperatureSensor.CELSIUS);
   }

   /*****************************************************************
   Set the units to English:  primarily for Temperature related
   data
   *****************************************************************/
   public void setUnitsEnglish(){
      //Set the temperature units first
      this.setTemperatureUnits(TemperatureSensor.FAHRENHEIT);
   }

   /*****************************************************************
   Set the current update rate in minutes
   *****************************************************************/
   public void setUpdateRate(int rate){
      this.updateRate = rate;
   }

   /*****************************************************************
   Implementation of the update() method as part of implementing
   the Runnable interface
   *****************************************************************/
   public void update(Observable o, Object arg){
      if(o instanceof SearchPort){
         SearchPort sp = (SearchPort)o;
         System.out.println(sp.getPortSearchResults());
      }
   }

   //**********************Private Methods***************************
   //
   //Find all possible ports
   //
   private Stack findPorts(){
      Stack portStack = new Stack();

      Enumeration ports = CommPortIdentifier.getPortIdentifiers();
      while(ports.hasMoreElements()){
         CommPortIdentifier cpi =
                             (CommPortIdentifier)ports.nextElement();
         if(cpi.getPortType() == CommPortIdentifier.PORT_SERIAL){
            portStack.push(new String(cpi.getName()));            
         }
      }
      return portStack;
   }

   //
   //Find all the possible one wire sensor information
   //
   private void findSensorInformation(){
      Hashtable typeHash = new Hashtable();
      SearchPort searchPort = new SearchPort();
      searchPort.addObserver(this);
      Stack portStack = this.findPorts();
      while(!portStack.empty()){
         String portString = (String)portStack.pop();
         searchPort.setCurrentPort(portString);
         String hashString = searchPort.grabPortData();
         if(!hashString.startsWith("Nothing")){
            String[] data = hashString.split("\\n");
            for(int i = 0; i < data.length; i += 2){
               try{
                  typeHash.put(data[i], data[i + 1]);
               }
               catch(NullPointerException npe){
                  npe.printStackTrace();
               }
            }
            this.sensorHash.put(portString, typeHash);
         }
      }
   }

   //
   //Find all the Temperature Sensors on the One Network
   //This conforms currently to Dallas Semiconductors:
   //DS18S20, DS1920.  Populate the appropriate Hashtable with
   //this information
   //
   private void findTempSensorInformation(){
      //Get an enumeration of the keys in the Hashtable
      Enumeration e = this.sensorHash.keys();
      while(e.hasMoreElements()){
         String key = (String)e.nextElement();
         try{
            Hashtable typeHash = (Hashtable)this.sensorHash.get(key);
            Enumeration ids = typeHash.keys();
            while(ids.hasMoreElements()){
               String id   = (String)ids.nextElement();
               String name = (String)typeHash.get(id);
               if(name.equals("DS1920") || name.equals("DS18S20")){
                  Stack tempStack = new Stack();
                  tempStack.push("Thermometer"); //Type
                  tempStack.push(name);
                  tempStack.push(id);
                  this.tempSensorHash.put(key, tempStack);
               }
            }
         }
         catch(NullPointerException npe){
            npe.printStackTrace();
         }
      }
   }

   //
   //Get the quit boolean (this is private, since the users of this
   //class need to know nothing about its state: just set the state)
   //
   private boolean getQuit(){
      return this.quit;
   }

   //
   //Set up the Temperature Sensors (Instantiate the appropriate
   //number of Temperature Sensors)
   //
   private void setUpTemperatureSensors(){
      TemperatureSensor ts;
      Enumeration e = this.tempSensorHash.keys();
      while(e.hasMoreElements()){
         String portName = (String)e.nextElement();
         try{
            Stack sensorData = 
                            (Stack)this.tempSensorHash.get(portName);
            String id   = (String)sensorData.pop();
            String name = (String)sensorData.pop();
            String type = (String)sensorData.pop();
            ts = new TemperatureSensor(portName, id, name, type);
            ts.resetBus(); //Reset the bus of every temp sensor
            this.tempSensorStack.push(ts);
         }
         catch(NullPointerException npe){
            npe.printStackTrace();
         }
      }
   }

   //
   //Set up the units for the temperature sensors.  This indicates
   //The units the Temperature Sensors should return the Temperature
   //Data and HOW the other temperature related data (Heat Index,
   //Dew Point, Wind Chill) should be archived.
   //
   public void setTemperatureUnits(int units){
      Enumeration e = tempSensorStack.elements();
      while(e.hasMoreElements()){
         TemperatureSensor ts = (TemperatureSensor)e.nextElement();
         try{
            ts.setUnits(units);
         }
         catch(UnitsException ue){
            ts.setUnits(TemperatureSensor.CELSIUS);
         }
      }
   }
}
